# Version:V1.1.0
# Author:LAI-1048576,2097152,Toby-LAI
# -*- coding:utf-8 -*-
import tkinter

from _tkinter import TclError
from tkinter import messagebox

def f():
    pass


__all__ = ['GUIError', 'GUI', 'msgbox', 'inputbox', 'ynbox', 'ccbox', 'errorbox', 'warningbox', 'infobox', 'CLICK_LEFT', 'CLICK_RIGHT', 'CLICK_MID', 'DOUBLE_CLICK_LEFT', 'DOUBLE_CLICK_RIGHT', 'DOUBLE_CLICK_MID', 'LEFT_MOVE', 'MID_MOVE', 'RIGHT_MOVE', 'Text', 'Button', 'Enter', 'Event', 'TextArea', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'SHIFT_A', 'SHIFT_B', 'SHIFT_C', 'SHIFT_D', 'SHIFT_E', 'SHIFT_F', 'SHIFT_G', 'SHIFT_H', 'SHIFT_I', 'SHIFT_J', 'SHIFT_K', 'SHIFT_L', 'SHIFT_M', 'SHIFT_N', 'SHIFT_O', 'SHIFT_P', 'SHIFT_Q', 'SHIFT_R', 'SHIFT_S', 'SHIFT_T', 'SHIFT_U', 'SHIFT_V', 'SHIFT_W', 'SHIFT_X', 'SHIFT_Y', 'SHIFT_Z', 'ALT_A', 'ALT_B', 'ALT_C', 'ALT_D', 'ALT_E', 'ALT_F', 'ALT_G', 'ALT_H', 'ALT_I', 'ALT_J', 'ALT_K', 'ALT_L', 'ALT_M', 'ALT_N', 'ALT_O', 'ALT_P', 'ALT_Q', 'ALT_R', 'ALT_S', 'ALT_T', 'ALT_U', 'ALT_V', 'ALT_W', 'ALT_X', 'ALT_Y', 'ALT_Z', 'CTRL_ALT_A', 'CTRL_ALT_B', 'CTRL_ALT_C', 'CTRL_ALT_D', 'CTRL_ALT_E', 'CTRL_ALT_F', 'CTRL_ALT_G', 'CTRL_ALT_H', 'CTRL_ALT_I', 'CTRL_ALT_J', 'CTRL_ALT_K', 'CTRL_ALT_L', 'CTRL_ALT_M', 'CTRL_ALT_N', 'CTRL_ALT_O', 'CTRL_ALT_P', 'CTRL_ALT_Q', 'CTRL_ALT_R', 'CTRL_ALT_S', 'CTRL_ALT_T', 'CTRL_ALT_U', 'CTRL_ALT_V', 'CTRL_ALT_W', 'CTRL_ALT_X', 'CTRL_ALT_Y', 'CTRL_ALT_Z', 'CTRL_SHIFT_A', 'CTRL_SHIFT_B', 'CTRL_SHIFT_C', 'CTRL_SHIFT_D', 'CTRL_SHIFT_E', 'CTRL_SHIFT_F', 'CTRL_SHIFT_G', 'CTRL_SHIFT_H', 'CTRL_SHIFT_I', 'CTRL_SHIFT_J', 'CTRL_SHIFT_K', 'CTRL_SHIFT_L', 'CTRL_SHIFT_M', 'CTRL_SHIFT_N', 'CTRL_SHIFT_O', 'CTRL_SHIFT_P', 'CTRL_SHIFT_Q', 'CTRL_SHIFT_R', 'CTRL_SHIFT_S', 'CTRL_SHIFT_T', 'CTRL_SHIFT_U', 'CTRL_SHIFT_V', 'CTRL_SHIFT_W', 'CTRL_SHIFT_X', 'CTRL_SHIFT_Y', 'CTRL_SHIFT_Z', 'CTRL_SHIFT_ALT_A', 'CTRL_SHIFT_ALT_B', 'CTRL_SHIFT_ALT_C', 'CTRL_SHIFT_ALT_D', 'CTRL_SHIFT_ALT_E', 'CTRL_SHIFT_ALT_F', 'CTRL_SHIFT_ALT_G', 'CTRL_SHIFT_ALT_H', 'CTRL_SHIFT_ALT_I', 'CTRL_SHIFT_ALT_J', 'CTRL_SHIFT_ALT_K', 'CTRL_SHIFT_ALT_L', 'CTRL_SHIFT_ALT_M', 'CTRL_SHIFT_ALT_N', 'CTRL_SHIFT_ALT_O', 'CTRL_SHIFT_ALT_P', 'CTRL_SHIFT_ALT_Q', 'CTRL_SHIFT_ALT_R', 'CTRL_SHIFT_ALT_S', 'CTRL_SHIFT_ALT_T', 'CTRL_SHIFT_ALT_U', 'CTRL_SHIFT_ALT_V', 'CTRL_SHIFT_ALT_W', 'CTRL_SHIFT_ALT_X', 'CTRL_SHIFT_ALT_Y', 'CTRL_SHIFT_ALT_Z', 'Release_A', 'Release_B', 'Release_C', 'Release_D', 'Release_E', 'Release_F', 'Release_G', 'Release_H', 'Release_I', 'Release_J', 'Release_K', 'Release_L', 'Release_M', 'Release_N', 'Release_O', 'Release_P', 'Release_Q', 'Release_R', 'Release_S', 'Release_T', 'Release_U', 'Release_V', 'Release_W', 'Release_X', 'Release_Y', 'Release_Z']


CLICK_LEFT = "<Button-1>"
CLICK_MID = "<Button-2>"
CLICK_RIGHT = "<Button-3>"
DOUBLE_CLICK_LEFT = "<Double-Button-1>"
DOUBLE_CLICK_MID = "<Double-Button-2>"
DOUBLE_CLICK_RIGHT = "<Double-Button-3>"
LEFT_MOVE = "<B1-Motion>"
MID_MOVE = "<B2-Motion>"
RIGHT_MOVE = "<B3-Motion>"
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z = "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
SHIFT_A, SHIFT_B, SHIFT_C, SHIFT_D, SHIFT_E, SHIFT_F, SHIFT_G, SHIFT_H, SHIFT_I, SHIFT_J, SHIFT_K, SHIFT_L, SHIFT_M, SHIFT_N, SHIFT_O, SHIFT_P, SHIFT_Q, SHIFT_R, SHIFT_S, SHIFT_T, SHIFT_U, SHIFT_V, SHIFT_W, SHIFT_X, SHIFT_Y, SHIFT_Z = "<Shift-A>", "<Shift-B>", "<Shift-C>", "<Shift-D>", "<Shift-E>", "<Shift-F>", "<Shift-G>", "<Shift-H>", "<Shift-I>", "<Shift-J>", "<Shift-K>", "<Shift-L>", "<Shift-M>", "<Shift-N>", "<Shift-O>", "<Shift-P>", "<Shift-Q>", "<Shift-R>", "<Shift-S>", "<Shift-T>", "<Shift-U>", "<Shift-V>", "<Shift-W>", "<Shift-X>", "<Shift-Y>", "<Shift-Z>"
ALT_A, ALT_B, ALT_C, ALT_D, ALT_E, ALT_F, ALT_G, ALT_H, ALT_I, ALT_J, ALT_K, ALT_L, ALT_M, ALT_N, ALT_O, ALT_P, ALT_Q, ALT_R, ALT_S, ALT_T, ALT_U, ALT_V, ALT_W, ALT_X, ALT_Y, ALT_Z = "<Alt-A>", "<Alt-B>", "<Alt-C>", "<Alt-D>", "<Alt-E>", "<Alt-F>", "<Alt-G>", "<Alt-H>", "<Alt-I>", "<Alt-J>", "<Alt-K>", "<Alt-L>", "<Alt-M>", "<Alt-N>", "<Alt-O>", "<Alt-P>", "<Alt-Q>", "<Alt-R>", "<Alt-S>", "<Alt-T>", "<Alt-U>", "<Alt-V>", "<Alt-W>", "<Alt-X>", "<Alt-Y>", "<Alt-Z>"
CTRL_A, CTRL_B, CTRL_C, CTRL_D, CTRL_E, CTRL_F, CTRL_G, CTRL_H, CTRL_I, CTRL_J, CTRL_K, CTRL_L, CTRL_M, CTRL_N, CTRL_O, CTRL_P, CTRL_Q, CTRL_R, CTRL_S, CTRL_T, CTRL_U, CTRL_V, CTRL_W, CTRL_X, CTRL_Y, CTRL_Z = "<Control-A>", "<Control-B>", "<Control-C>", "<Control-D>", "<Control-E>", "<Control-F>", "<Control-G>", "<Control-H>", "<Control-I>", "<Control-J>", "<Control-K>", "<Control-L>", "<Control-M>", "<Control-N>", "<Control-O>", "<Control-P>", "<Control-Q>", "<Control-R>", "<Control-S>", "<Control-T>", "<Control-U>", "<Control-V>", "<Control-W>", "<Control-X>", "<Control-Y>", "<Control-Z>"
CTRL_SHIFT_A, CTRL_SHIFT_B, CTRL_SHIFT_C, CTRL_SHIFT_D, CTRL_SHIFT_E, CTRL_SHIFT_F, CTRL_SHIFT_G, CTRL_SHIFT_H, CTRL_SHIFT_I, CTRL_SHIFT_J, CTRL_SHIFT_K, CTRL_SHIFT_L, CTRL_SHIFT_M, CTRL_SHIFT_N, CTRL_SHIFT_O, CTRL_SHIFT_P, CTRL_SHIFT_Q, CTRL_SHIFT_R, CTRL_SHIFT_S, CTRL_SHIFT_T, CTRL_SHIFT_U, CTRL_SHIFT_V, CTRL_SHIFT_W, CTRL_SHIFT_X, CTRL_SHIFT_Y, CTRL_SHIFT_Z = 'Ctrl-Shift-A', 'Ctrl-Shift-B', 'Ctrl-Shift-C', 'Ctrl-Shift-D', 'Ctrl-Shift-E', 'Ctrl-Shift-F', 'Ctrl-Shift-G', 'Ctrl-Shift-H', 'Ctrl-Shift-I', 'Ctrl-Shift-J', 'Ctrl-Shift-K', 'Ctrl-Shift-L', 'Ctrl-Shift-M', 'Ctrl-Shift-N', 'Ctrl-Shift-O', 'Ctrl-Shift-P', 'Ctrl-Shift-Q', 'Ctrl-Shift-R', 'Ctrl-Shift-S', 'Ctrl-Shift-T', 'Ctrl-Shift-U', 'Ctrl-Shift-V', 'Ctrl-Shift-W', 'Ctrl-Shift-X', 'Ctrl-Shift-Y', 'Ctrl-Shift-Z'
CTRL_ALT_A, CTRL_ALT_B, CTRL_ALT_C, CTRL_ALT_D, CTRL_ALT_E, CTRL_ALT_F, CTRL_ALT_G, CTRL_ALT_H, CTRL_ALT_I, CTRL_ALT_J, CTRL_ALT_K, CTRL_ALT_L, CTRL_ALT_M, CTRL_ALT_N, CTRL_ALT_O, CTRL_ALT_P, CTRL_ALT_Q, CTRL_ALT_R, CTRL_ALT_S, CTRL_ALT_T, CTRL_ALT_U, CTRL_ALT_V, CTRL_ALT_W, CTRL_ALT_X, CTRL_ALT_Y, CTRL_ALT_Z = 'Ctrl-Alt-A', 'Ctrl-Alt-B', 'Ctrl-Alt-C', 'Ctrl-Alt-D', 'Ctrl-Alt-E', 'Ctrl-Alt-F', 'Ctrl-Alt-G', 'Ctrl-Alt-H', 'Ctrl-Alt-I', 'Ctrl-Alt-J', 'Ctrl-Alt-K', 'Ctrl-Alt-L', 'Ctrl-Alt-M', 'Ctrl-Alt-N', 'Ctrl-Alt-O', 'Ctrl-Alt-P', 'Ctrl-Alt-Q', 'Ctrl-Alt-R', 'Ctrl-Alt-S', 'Ctrl-Alt-T', 'Ctrl-Alt-U', 'Ctrl-Alt-V', 'Ctrl-Alt-W', 'Ctrl-Alt-X', 'Ctrl-Alt-Y', 'Ctrl-Alt-Z'
CTRL_SHIFT_ALT_A, CTRL_SHIFT_ALT_B, CTRL_SHIFT_ALT_C, CTRL_SHIFT_ALT_D, CTRL_SHIFT_ALT_E, CTRL_SHIFT_ALT_F, CTRL_SHIFT_ALT_G, CTRL_SHIFT_ALT_H, CTRL_SHIFT_ALT_I, CTRL_SHIFT_ALT_J, CTRL_SHIFT_ALT_K, CTRL_SHIFT_ALT_L, CTRL_SHIFT_ALT_M, CTRL_SHIFT_ALT_N, CTRL_SHIFT_ALT_O, CTRL_SHIFT_ALT_P, CTRL_SHIFT_ALT_Q, CTRL_SHIFT_ALT_R, CTRL_SHIFT_ALT_S, CTRL_SHIFT_ALT_T, CTRL_SHIFT_ALT_U, CTRL_SHIFT_ALT_V, CTRL_SHIFT_ALT_W, CTRL_SHIFT_ALT_X, CTRL_SHIFT_ALT_Y, CTRL_SHIFT_ALT_Z = 'Ctrl-Shift-Alt-A', 'Ctrl-Shift-Alt-B', 'Ctrl-Shift-Alt-C', 'Ctrl-Shift-Alt-D', 'Ctrl-Shift-Alt-E', 'Ctrl-Shift-Alt-F', 'Ctrl-Shift-Alt-G', 'Ctrl-Shift-Alt-H', 'Ctrl-Shift-Alt-I', 'Ctrl-Shift-Alt-J', 'Ctrl-Shift-Alt-K', 'Ctrl-Shift-Alt-L', 'Ctrl-Shift-Alt-M', 'Ctrl-Shift-Alt-N', 'Ctrl-Shift-Alt-O', 'Ctrl-Shift-Alt-P', 'Ctrl-Shift-Alt-Q', 'Ctrl-Shift-Alt-R', 'Ctrl-Shift-Alt-S', 'Ctrl-Shift-Alt-T', 'Ctrl-Shift-Alt-U', 'Ctrl-Shift-Alt-V', 'Ctrl-Shift-Alt-W', 'Ctrl-Shift-Alt-X', 'Ctrl-Shift-Alt-Y', 'Ctrl-Shift-Alt-Z'
Release_A, Release_B, Release_C, Release_D, Release_E, Release_F, Release_G, Release_H, Release_I, Release_J, Release_K, Release_L, Release_M, Release_N, Release_O, Release_P, Release_Q, Release_R, Release_S, Release_T, Release_U, Release_V, Release_W, Release_X, Release_Y, Release_Z = "KeyRelease-A", "KeyRelease-B", "KeyRelease-C", "KeyRelease-D", "KeyRelease-E", "KeyRelease-F", "KeyRelease-G", "KeyRelease-H", "KeyRelease-I", "KeyRelease-J", "KeyRelease-K", "KeyRelease-L", "KeyRelease-M", "KeyRelease-N", "KeyRelease-O", "KeyRelease-P", "KeyRelease-Q", "KeyRelease-R", "KeyRelease-S", "KeyRelease-T", "KeyRelease-U", "KeyRelease-V", "KeyRelease-W", "KeyRelease-X", "KeyRelease-Y", "KeyRelease-Z"
ENTER, ANY_KEY = "<Return>", "<Key>"


class Text(object):
    def __init__(self, gui, label):
        self.g = gui
        self.label = label

    def bind(self, event, func):
        self.label.bind(event.NAME, func)


class Enter(object):
    def __init__(self, gui, entry):
        self.g = gui
        self.entry = entry

    def bind(self, event):
        pass


class Button(object):
    def __init__(self, gui, button):
        self.g = gui
        self.button = button

    def bind(self, event, func):
        self.button.bind(event.NAME, func)


class PassWord(object):
    def __init__(self, gui, entry):
        self.g = gui
        self.entry = entry

    def bind(self, event, func):
        self.entry.bind(event.NAME, func)


class TextArea(object):
    def __init__(self, gui, text):
        self.g = gui
        self.text = text

    def bind(self, event, func):
        self.text.bind(event.NAME, func)


class Event(object):
    def __init__(self, event):
        if True:
            self.NAME = event


class GUIError(TclError):
    pass


class GUI(object):
    class Var(object):
        def __init__(self, gui):
            self.V = tkinter.Variable(gui.t)

        def __getattr__(self, item):
            if item == "value":
                return self.V.get()
            else:
                raise GUIError("Only value attribute can be used")

        def __setattr__(self, key, value):
            if key == "value":
                self.V.set(value)
            elif key == "V" and isinstance(value, tkinter.Variable):  # Security check
                self.__dict__["V"] = value
            else:
                raise GUIError("Only value attribute can be used")

        def __add__(self, other):
            try:
                return self.value + other.value
            except:
                raise GUIError(f"cannot add between {type(self.value)} and {type(other.value)}")

        def __sub__(self, other):
            try:
                return self.value - other.value
            except:
                raise GUIError(f"cannot minus between {type(self.value)} and {type(other.value)}")

        def __mul__(self, other):
            try:
                return self.value * other.value
            except:
                raise GUIError(f"cannot times between {type(self.value)} and {type(other.value)}")

        def __truediv__(self, other):
            try:
                return self.value / other.value
            except:
                raise GUIError(f"cannot div between {type(self.value)} and {type(other.value)}")

        def __floordiv__(self, other):
            try:
                return self.value // other.value
            except:
                raise GUIError(f"cannot floordiv between {type(self.value)} and {type(other.value)}")

        def __abs__(self):
            try:
                return abs(self.value)
            except:
                raise GUIError(f"cannot abs with {type(self.value)}")

        def __eq__(self, other):
            return self.value == other.value

        def __gt__(self, other):
            return self.value > other.value

        def __ge__(self, other):
            return self.value >= other.value

        def __lt__(self, other):
            return self.value < other.value

        def __ne__(self, other):
            return self.value != other.value

        def __le__(self, other):
            return self.value <= other.value

    def __init__(self, width=300, height=300, title="Window",toplevel_tk=None):
        if toplevel_tk is None:
            try:
                self.t = tkinter.Tk()
                self.t.geometry(f"{width}x{height}")
                self.t.title(title)
            except TclError:
                raise GUIError("Invalid Parameter")
        else:
            self.t = toplevel_tk

    

    def run(self):
        self.t.mainloop()

    def close(self):
        self.t.destroy()

    def text(self, text="", x=None, y=None, color="black", var=None, font="宋体", size=8):
        if x is not None and y is not None:
            l = tkinter.Label(master = self.t, text = text, fg = color, textvariable = None if var is None else var.V,
                              font = (font, size))
            l.place(x = x, y = y)
            return Text(self, l)
        elif not (x or y):
            l = tkinter.Label(master = self.t, text = text, fg = color, textvariable = None if var is None else var.V,
                              font = (font, size))
            l.pack()
            return Text(self, l)
        else:
            raise GUIError("Please don't just fill in one of X / Y")

    def button(self, command=f, width=20, height=20, text="", x=None, y=None, var=None, font="宋体", size=8):
        if x is not None and y is not None:
            b = tkinter.Button(master = self.t, text = text, command = command, width = width, height = height,
                               textvariable = None if var is None else var.V, font = (font, size))
            b.place(x = x, y = y)
            return Button(self, b)
        elif not (x or y):
            b = tkinter.Button(master = self.t, text = text, command = command,
                               textvariable = None if var is None else var.V, font = (font, size))
            b.pack()
            return Button(self, b)
        else:
            raise GUIError("Please don't just fill in one of X / Y")

    def enter(self, x=None, y=None, var=None, font="宋体", size=8):
        if x is not None and y is not None:
            e = tkinter.Entry(master = self.t, textvariable = tkinter.Variable() if var is None else var.V,
                              font = (font, size))
            e.place(x = x, y = y)
            return Enter(self, e)
        elif not (x or y):
            e = tkinter.Entry(master = self.t, textvariable = None if var is None else var.V, font = (font, size))
            e.pack()
            return Enter(self, e)

        else:
            raise GUIError("Please don't just fill in one of X / Y")

    def password(self, x=None, y=None, var=None, font="宋体", size=8):
        if x is not None and y is not None:
            e = tkinter.Entry(master = self.t, show = "●", textvariable = None if var is None else var.V,
                              font = (font, size))
            e.place(x = x, y = y)
            return PassWord(self, e)
        elif not (x or y):
            e = tkinter.Entry(master = self.t, show = "●", textvariable = None if var is None else var.V,
                              font = (font, size))
            e.pack()
            return PassWord(self, e)
        else:
            raise GUIError("Please don't just fill in one of X / Y")

    def textarea(self, x=None, y=None, width=20, height=4, font="宋体", size=8):
        if x and y:
            t = tkinter.Text(master = self.t, width = width, height = height, font = (font, size))
            t.place(x = x, y = y)
            return TextArea(self, t)
        elif not (x or y):
            t = tkinter.Text(master = self.t, width = width, height = height, font = (font, size))
            t.pack()
            return TextArea(self, t)
        else:
            raise GUIError("Please don't just fill in one of X / Y")

    def radiobutton(self, *dicts):
        """
        单选框，填入数据字典。
        返回单选框对象列表
        """
        for dict_ in dicts:
            pass


def msgbox(text, title="", button_text="OK"):
    a = tkinter.Tk()
    a.title(title)
    a.geometry(f"{len(str(text))*10+50}x100")
    tkinter.Label(a, text = text).pack()
    tkinter.Button(a, text = button_text, command = a.destroy).pack()
    a.mainloop()


def inputbox(type_=str, text="", title="", max=None, min=None):
    result = None

    def submit():
        try:
            nonlocal result

            if (max is None or type_(e.get()) < max) and (min is None or type_(e.get()) > min):  # valid value
                result = type_(e.get())
            else:
                raise GUIError(f"Must be in {min}~{max}")
        except Exception as err:
            raise GUIError(str(err))
        finally:
            a.destroy()

    a = tkinter.Tk()
    a.title(title)
    a.geometry("300x100")
    tkinter.Label(a, text = text).pack()
    e = tkinter.Entry(a)
    e.pack()
    tkinter.Button(a, text = "确定", command = submit).pack()
    a.mainloop()
    return result


def ynbox(text="", title="", btns=("Yes", "No")):
    result = None
    a = tkinter.Tk()
    a.title(title)
    a.geometry("300x100")

    def r(res):
        nonlocal result
        a.destroy()
        result = res

    tkinter.Label(master = a, text = text).grid(row = 1, column = 1, columnspan = 3)
    tkinter.Button(master = a, text = btns[0], command = lambda: r(True)).grid(row = 2, column = 1)
    tkinter.Label(master = a, text = " " * 55).grid(row = 2, column = 2)
    tkinter.Button(master = a, text = btns[1], command = lambda: r(False)).grid(row = 2, column = 3)

    a.mainloop()
    return result


def ccbox(text="", title="", btns=("Continue", "Cancel")):
    result = None
    a = tkinter.Tk()
    a.title(title)
    a.geometry("300x100")

    def r(res):
        nonlocal result
        a.destroy()
        result = res

    tkinter.Label(master = a, text = text).grid(row = 1, column = 1, columnspan = 3)
    tkinter.Button(master = a, text = btns[0], command = lambda: r(1)).grid(row = 2, column = 1)
    tkinter.Label(master = a, text = " " * 45).grid(row = 2, column = 2)
    tkinter.Button(master = a, text = btns[1], command = lambda: r(0)).grid(row = 2, column = 3)

    a.mainloop()
    return result


def errorbox(text="", title=""):
    messagebox.showerror(title = title, message = text)


def warningbox(text="", title=""):
    messagebox.showwarning(title = title, message = text)


def infobox(text="", title=""):
    messagebox.showinfo(title = title, message = text)

def toplevel(gui):
    return GUI(tkinter.toplevel(gui.t))